home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
C
/
Snippets
/
Simple C Sound
/
Sound.c
< prev
Wrap
Text File
|
1996-06-25
|
9KB
|
331 lines
/*
Sound.c
Simple plug-in source to play sounds (2-channels) asynchronously. OK, so it won't do for a highly polished game
which needs four-channel double-buffering, but it suffices for most stuff.
By Ross Younger, ©1996. You are free to use any or all of this source code in your own projects. Just don't ask me
to support it.
Caveat: Of course, async sounds don't by default report back when they are finished. You might want to modify the
callback procs if you feel so inclined. Good luck!
** DO NOT ADD THIS FILE DIRECTLY TO YOUR PROJECT **
Copy it, and add the copy, because you need to alter the constants.
*/
#include <MixedMode.h>
#include <Sound.h>
#define kNumberSounds 3 // number of sounds to load
#define kFirstSoundID 1000 // ID of first sound
/*
Change the constants to match your resources. The sounds are assumed to be sequential, starting with kFirstSoundID.
Call InitSound() during your init routine. This will load in all the sounds, taking up space in your heap zone.
Call KillSound() during your cleanup.
To play a sound, use PlayASound(soundNumber, priority);
where soundNumber is the number within the file (starting at 0); eg. with sounds ID 100 thru 105,
soundNumber 4 would give 'snd ' ID 103.
PlayASound handles priorities like this:
if [ (channel1clear) or (newpriority >= sound1currentpriority) ], play sound on channel 1
else if [ (channel2clear) or (newpriority >= sound1currentpriority) ], play sound on channel 2
else ignore the sound request.
Or, for those of us who speak English: a sound of a higher (or equal) priority will override one that is still playing.
The PlayASound routine tries to find a free channel, or one whose priority is greater than or equal to the one already
playing there.
* Check out the InitSound; it has error traps that give off weird return values. *
*/
// Constants
#define kSoundDone 913
#define kSoundDone2 749
// Prototypes
short InitSound (void);
void KillSound (void);
void PlayASound (short soundID, short priority);
void PlaySound1 (short, short);
void PlaySound2 (short, short);
pascal void ExternalCallBack (SndChannelPtr, SndCommand);
pascal void ExternalCallBack2 (SndChannelPtr, SndCommand);
void LoadAllSounds (void);
OSErr LoadBufferSounds (void);
OSErr DumpBufferSounds (void);
OSErr OpenSoundChannel (void);
OSErr CloseSoundChannel (void);
// Globals for this source file
SndCallBackUPP externalCallBackUPP, externalCallBackUPP2;
SndChannelPtr externalChannel, externalChannel2;
Ptr theSoundData[kNumberSounds];
short externalPriority, externalPriority2;
Boolean channelOpen, soundOn;
/**********************************************************************************/
/*
You *can* use PlaySound1 and PlaySound2 directly, but I wouldn't recommend it.
*/
void PlaySound1 (short soundID, short priority)
{
SndCommand theCommand;
OSErr theErr;
theCommand.cmd = flushCmd;
theCommand.param1 = 0;
theCommand.param2 = 0L;
theErr = SndDoImmediate(externalChannel, &theCommand);
theCommand.cmd = quietCmd;
theCommand.param1 = 0;
theCommand.param2 = 0L;
theErr = SndDoImmediate(externalChannel, &theCommand);
externalPriority = priority;
theCommand.cmd = bufferCmd;
theCommand.param1 = 0;
theCommand.param2 = (long)(theSoundData[soundID]);
theErr = SndDoImmediate(externalChannel, &theCommand);
theCommand.cmd = callBackCmd;
theCommand.param1 = kSoundDone;
theCommand.param2 = SetCurrentA5();
theErr = SndDoCommand(externalChannel, &theCommand, true);
}
/**********************************************************************************/
void PlaySound2 (short soundID, short priority)
{
SndCommand theCommand;
OSErr theErr;
theCommand.cmd = flushCmd;
theCommand.param1 = 0;
theCommand.param2 = 0L;
theErr = SndDoImmediate(externalChannel2, &theCommand);
theCommand.cmd = quietCmd;
theCommand.param1 = 0;
theCommand.param2 = 0L;
theErr = SndDoImmediate(externalChannel2, &theCommand);
externalPriority2 = priority;
theCommand.cmd = bufferCmd;
theCommand.param1 = 0;
theCommand.param2 = (long)(theSoundData[soundID]);
theErr = SndDoImmediate(externalChannel2, &theCommand);
theCommand.cmd = callBackCmd;
theCommand.param1 = kSoundDone2;
theCommand.param2 = SetCurrentA5();
theErr = SndDoCommand(externalChannel2, &theCommand, true);
}
/**********************************************************************************/
void PlayASound (short soundID, short priority)
// Remember! Pass as soundID the sound-number-in-file, NOT the ID!
{
if ((soundID >= 0) && (soundID < kNumberSounds)) // Ignore invalid IDs
{
if (soundOn)
{
if (externalPriority < externalPriority2)
{
if (priority >= externalPriority)
PlaySound1(soundID, priority);
}
else
{
if (priority >= externalPriority2)
PlaySound2(soundID, priority);
}
}
}
}
/**********************************************************************************/
// Don't worry about this macro! Just accept it!
RoutineDescriptor ExternalCallBackRD =
BUILD_ROUTINE_DESCRIPTOR(uppFilePlayCompletionProcInfo, ExternalCallBack);
pascal void ExternalCallBack (SndChannelPtr theChannel, SndCommand theCommand)
{
long thisA5, wasA5;
if (theCommand.param1 == kSoundDone)
{
wasA5 = theCommand.param2;
thisA5 = SetA5(wasA5);
externalPriority = 0;
thisA5 = SetA5(thisA5);
}
}
/**********************************************************************************/
RoutineDescriptor ExternalCallBackRD2 =
BUILD_ROUTINE_DESCRIPTOR(uppFilePlayCompletionProcInfo, ExternalCallBack2);
pascal void ExternalCallBack2 (SndChannelPtr theChannel, SndCommand theCommand)
{
long thisA5, wasA5;
if (theCommand.param1 == kSoundDone2)
{
wasA5 = theCommand.param2;
thisA5 = SetA5(wasA5);
externalPriority2 = 0;
thisA5 = SetA5(thisA5);
}
}
/**********************************************************************************/
OSErr LoadBufferSounds (void)
{
Handle theSound;
long soundDataSize;
OSErr theErr;
short i;
theErr = noErr;
for (i = 0; i < kNumberSounds; i++)
{
theSound = GetResource('snd ', i + kFirstSoundID);
if (theSound == 0L)
return (ResError());
HLock(theSound);
soundDataSize = GetHandleSize(theSound) - 20L;
HUnlock(theSound);
theSoundData[i] = NewPtr(soundDataSize);
if (theSoundData[i] == 0L)
return (ResError());
HLock(theSound);
BlockMove((Ptr)(*theSound + 20L), theSoundData[i], soundDataSize);
HUnlock(theSound);
ReleaseResource(theSound);
}
return (theErr);
}
/**********************************************************************************/
OSErr DumpBufferSounds (void)
{
OSErr theErr;
short i;
theErr = noErr;
for (i = 0; i < kNumberSounds; i++)
{
if (theSoundData[i] != 0L)
DisposPtr(theSoundData[i]);
theSoundData[i] = 0L;
}
return (theErr);
}
/**********************************************************************************/
OSErr OpenSoundChannel (void)
{
OSErr theErr;
#if USESROUTINEDESCRIPTORS
externalCallBackUPP = &ExternalCallBackRD;
externalCallBackUPP2 = &ExternalCallBackRD2;
#else
externalCallBackUPP = (SndCallBackUPP) &ExternalCallBack;
externalCallBackUPP2 = (SndCallBackUPP) &ExternalCallBack2;
#endif
theErr = noErr;
if (channelOpen)
return (theErr);
externalChannel = 0L;
theErr = SndNewChannel(&externalChannel,
sampledSynth, initNoInterp + initMono,
(SndCallBackUPP)externalCallBackUPP);
if (theErr == noErr)
channelOpen = true;
externalChannel2 = 0L;
theErr = SndNewChannel(&externalChannel2,
sampledSynth, initNoInterp + initMono,
(SndCallBackUPP)externalCallBackUPP2);
if (theErr == noErr)
channelOpen = true;
return (theErr);
}
/**********************************************************************************/
OSErr CloseSoundChannel (void)
{
OSErr theErr;
theErr = noErr;
if (!channelOpen)
return (theErr);
if (externalChannel != 0L)
theErr = SndDisposeChannel(externalChannel, true);
externalChannel = 0L;
if (externalChannel2 != 0L)
theErr = SndDisposeChannel(externalChannel2, true);
externalChannel2 = 0L;
if (theErr == noErr)
channelOpen = false;
return (theErr);
}
/**********************************************************************************/
short InitSound (void)
/*
This function loads the sounds and opens the channels. Return value is 0 for success, -1 for couldn't load the sounds,
and -2 for couldn't open the channels.
*/
{
OSErr theErr;
soundOn = true;
externalChannel = 0L;
externalChannel2 = 0L;
externalPriority = 0;
externalPriority2 = 0;
theErr = LoadBufferSounds();
if (theErr != noErr) // Failed to load the sounds (missing resource?). You might want to change how this is handled.
return (-1);
theErr = OpenSoundChannel();
if (theErr != noErr) // Couldn't open the sound channels (already in use?). Again, you may want to change how this is handled.
return (-2);
return (0); // 0 for success!
}
/**********************************************************************************/
void KillSound (void)
{
OSErr theErr;
theErr = DumpBufferSounds();
theErr = CloseSoundChannel();
} // Errors are ignored as we're closing down anyway.
/**********************************************************************************/
// The end.